Skip to content

perf(vk): cap the system-heap budget for idle live imports on Linux - #2839

Merged
hckrman101 merged 3 commits into
NotRexed:mainfrom
Daydatao:perf/linux-idle-imports-cap
Nov 12, 2026
Merged

hckrman101 merged 3 commits into
NotRexed:mainfrom
Daydatao:perf/linux-idle-imports-cap

Conversation

@Daydatao

@Daydatao Daydatao commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

On Linux PCs with 32 GB+ RAM (budget >= 12 GB), imports_give_back was off by default, letting 200+ idle unmirrored pools from earlier areas stay in imported over a two-hour session (7.4 GB live): on RADV (amdgpu), that doubled queue submit time (0.55 → 1.14 ms), tripled GPU write-back waits (2.19 → 6.29–7.03 ms), grew the submit-time capture scan from 2.4 GB to 7.2 GB, and faded the frame rate from 60+ fps down to 44–48 fps. This keeps 16 GB PCs and the Steam Deck (budget < 12 GB) unchanged, turns imports_give_back on for budget >= 12 GB Linux PCs with a default 4,500 MB cap (~3,825 MB high / ~3,375 MB low), and adds RX_VK_IMPORT_CAP_MB (0 = off) so the cap can be tuned or disabled without rebuilding.

Part of #993
Part of #1000

What changed

  • retired_give_back::live_budget (gpu/vk/retired_give_back.h, gpu/vk/vk_memory.cpp).
    • Under a 12 GB system-heap budget (16 GB PCs and the Steam Deck): unchanged (live_budget returns budget, so 85% / 75% applies to the full ~7.5 GB budget as in vk: imports given back under system-heap pressure on 16 GB PCs #479, unless RX_VK_IMPORT_CAP_MB > 0 is explicitly set).
    • At 12 GB and above (32 GB+ Linux PCs, where imports_give_back was off): live_budget caps the effective budget at 4,500 MB by default (~3,825 MB high / ~3,375 MB low) so unmirrored live pools whose VRAM mirrors were already freed by mirrors_give_back (mstate == MIRROR_NONE, was_mirrored == true) do not stay in RADV's per-submit BO list and cap_ranges for the whole session.
    • RX_VK_IMPORT_CAP_MB: RX_VK_IMPORT_CAP_MB=<MB> (> 0) sets the cap in MB on any budget so testers can try other values without rebuilding; RX_VK_IMPORT_CAP_MB=0 turns the >= 12 GB cap off (restoring main's behavior on 32 GB+ PCs while keeping < 12 GB PCs unchanged).
    • Explicit RX_VK_IMPORT_HIGH / RX_VK_IMPORT_LOW: setting either watermark switch without RX_VK_IMPORT_CAP_MB lifts the default 4,500 MB cap and uses the full system-heap budget (RX_VK_IMPORT_HIGH=0 still turns the valve off everywhere). Windows remains off by default (high = 0 unless RX_VK_IMPORT_HIGH is set).
  • imports_give_back reads its switches via env_int and shares retired_give_back::want, covered by tests/test_retired_give_back.py.

Tested

Arch Linux (KDE Plasma Wayland, 2560x1440 window, /dev/udmabuf active), AMD Radeon RX 9070 XT 16 GB (Mesa RADV 26.2.6), Intel Core i5-13600KF, 32 GB DDR4 (15,434 MB system heap budget), v2.1.0 (source e561602e), multi-area play across Hyrule:

Metric (hookshot_log.txt) Baseline at 10 min (40,920 presents, default hookshot_env.txt) Baseline after ~2 h (684,120 presents, default hookshot_env.txt, imports_give_back off) Capped run after ~1.5 h (176,640 presents, RX_VK_IMPORT_HIGH=25, RX_VK_IMPORT_LOW=20 → ~3,858 / ~3,086 MB; RX_VK_MIRROR_MB=4096, RX_VK_RECORDER=thread, RX_VK_BATCH=0)
vk: imports: ... live 55 live (2000 MB) 221 live (7392 MB) (67 retired given back, 2992 MB) 50–64 live (2256–2720 MB)
Idle live imports given back off (budget >= 12 GB) off 257 (7872 MB) in all (system heap 3878 of 15434 MB)
capture: a submit 0.27 ms (scan 0.05 over 734 ranges, 2417 MB) 0.50 ms (scan 0.09 over 334 ranges, 7218 MB) 0.18–0.43 ms (scan 0.08 over 3923–4251 MB)
queue submit 0.55 ms 1.11–1.14 ms 0.32–0.73 ms
readbacks of GPU results wait 2.19 ms each (270 of 3000) 6.29–7.03 ms each (312–340 of 3000) 0.64–1.14 ms each
Process working set / swap ~10.2 GB / 0 MB 15351 MB (peak 16107) / 1511 MB swap Bounded, 0 MB swap
Frame rate 60+ fps 44–48.4 fps at 303–408 draws (ratio 1.352, game's frame rate 44) 63.5–65.6 fps (641–708 draws), 67.2–68.3 fps (186 draws)
  • All 29 gpu/vk/vk_memory.cpp and retired_give_back checks pass (tests/test_retired_give_back.py, tests/test_texel_buffer_ownership.py, tests/test_import_buffer_binding.py, tests/test_mem_trim.py, tests/test_vram_budget_row.py, etc.).
  • Added two rows to docs/tested-configurations.md.
  • Nothing on screen changes: no screenshots.

Cost where not measured / caveats:

  • Windows is unchanged (still off by default).
  • 16 GB PCs and the Steam Deck (budget < 12 GB) are unchanged by default (live_budget returns the full budget below 12 GB).
  • Not tested on 32 GB+ Linux with NVIDIA or Intel drivers (only RX 9070 XT / Mesa RADV 26.2.6 is available on this machine).

@Daydatao

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! I pushed 6c9c7f4 to address your points:

  1. 16 GB PCs and the Steam Deck (budget < 12 GB) are left untouched by default: retired_give_back::live_budget now applies the default 4,500 MB cap only when budget >= 12 GB (kSmallBudget = 12ull << 30), i.e. the 32 GB+ systems where imports_give_back was completely off before. Under 12 GB it returns budget as in vk: imports given back under system-heap pressure on 16 GB PCs #479, so integrated GPUs / the Deck and 16 GB PCs keep their existing ~6.4 GB threshold and aren't churned.
  2. Added RX_VK_IMPORT_CAP_MB (env_int("RX_VK_IMPORT_CAP_MB", -1)):
    • RX_VK_IMPORT_CAP_MB=<MB> (> 0) overrides the cap in MB on any budget so testers can try other values without rebuilding.
    • RX_VK_IMPORT_CAP_MB=0 turns the >= 12 GB cap off (restoring main's behavior on 32 GB+ systems while leaving < 12 GB systems as before).
  3. RX_VK_IMPORT_HIGH / RX_VK_IMPORT_LOW escape hatch & block comment: setting either RX_VK_IMPORT_HIGH or RX_VK_IMPORT_LOW without RX_VK_IMPORT_CAP_MB now lifts the default 4,500 MB cap and uses the full system-heap budget, and the block comment above imports_give_back spells both out explicitly.
  4. Baseline table row (docs/tested-configurations.md & PR description): clarified that the 2 h baseline (684,360 presents) ran with the default hookshot_env.txt (default 2,048 MB mirror budget, default recorder), whereas the 1.5 h capped run had RX_VK_MIRROR_MB=4096, RX_VK_RECORDER=thread, RX_VK_BATCH=0 alongside the import watermarks.

Regarding #2759 and hardware coverage:

@hckrman101
hckrman101 merged commit 11c0ab4 into NotRexed:main Nov 12, 2026
1 check passed
elkarbeenus pushed a commit to elkarbeenus/LakuenRecompBuilder that referenced this pull request Nov 12, 2026
….3.0

Fewer idle threads spinning (NotRexed#2709), bounded waits for shaders still translating (NotRexed#2586), the first start after a
preparation warms the pipeline cache (NotRexed#2865, NotRexed#2869), native float atomics (NotRexed#2306), the 97% squeeze past the allowance
(NotRexed#2585), output choices from the screen's modes (NotRexed#2566), FSR 4 twice as fast on RDNA 2 (NotRexed#2798), the import cap
(NotRexed#2839) and the final NotRexed#2825 (mirror trims settle). Kept: the fork's play() returning the game for Steam's wait (now
through start_scoped's process), the handheld texture caps, the RDNA2 detection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7fdTPXbwDRohew9qanvbp
dazemc added a commit to dazemc/Hookshot-rs that referenced this pull request Nov 12, 2026
Port four upstream fixes that the GCN/RADV notes did not cover:

- NotRexed#2685: import game memory in 4 MB chunks where a pool has a VRAM
  mirror (16 MB without one, as before). A mirror is its import's whole
  range, so the old 16 MB chunks left 28-39% of the mirrors outside
  every game pool; 4 MB cuts them by roughly a quarter at the same frame
  rate. RX_VK_IMPORT_CHUNK_MB still chooses.
- NotRexed#2795: a retired pool whose mirror was refused gives its import back
  too, as a MIRROR_NONE pool does. Left out, every such pool kept its
  pinned userptr import (shared GPU memory) for the session.
- NotRexed#2825: an import mostly outside every game pool, while VRAM is over
  80% of its allowance, is trimmed to its live parts; the new mirrors
  inherit what the GPU wrote and the old one goes once every part has
  its own. RX_VK_MIRROR_TRIM=0 turns it off.
- NotRexed#2839: on Linux at a 12 GB+ system heap budget, idle live imports are
  capped at 4500 MB (RX_VK_IMPORT_CAP_MB). On RADV/amdgpu 200+ idle
  unmirrored pools doubled queue submit time over a two-hour session;
  the measured run went from 44-48 to 63-68 fps (RX 9070 XT).

Game-free fixtures: tests/import_chunk_test.cpp, tests/test_mirror_trim.cpp
and the extended tests/retired_give_back_test.cpp run from
builder/rust/tests/amd_vram.rs, which also source-checks the wiring, the
refused-mirror condition and RADV's Linux AMD mesh default (issue 2245).
The Vulkan backend builds with RX_VK_ONLY. No AMD hardware here: the
RX 9070 XT and RX 580 measurements are upstream's.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant